package com.google.code.wicket.auth.component.value.resolver.annotation;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * A cache for annotations on types.
 * 
 * @param <T>
 */
public class AnnotationCache<T extends Annotation> {

    /**
     * Factory method to get an annotation cache for a specified annotation
     * class.
     * 
     * @param <A>
     * @param annotationType
     * @return
     */
    public static <A extends Annotation> AnnotationCache<A> forAnnotationType(
            final Class<A> annotationType) {
        return new AnnotationCache<A>(annotationType);
    }

    private final Class<T> annotationType;

    private final Map<Class<?>, T> classMap;

    /**
     * Constructor with an annotation type.
     * 
     * @param annotationType
     */
    protected AnnotationCache(final Class<T> annotationType) {
        this.annotationType = annotationType;
        this.classMap = new WeakHashMap<Class<?>, T>();
    }

    /**
     * Get the specified annotation on the specified type.
     * 
     * @param type
     * @return the annotation or null if not defined
     */
    public T getAnnotationForType(final Class<?> type) {
        T result;
        synchronized (this.classMap) {
            if (this.classMap.containsKey(type)) {
                result = this.classMap.get(type);
            } else {
                result = this.readAnnotation(type);
                this.classMap.put(type, result);
            }
        }
        return result;
    }

    private T readAnnotation(final Class<?> type) {
        T result;
        if (Object.class.equals(type)) {
            result = null;
        } else {
            result = type.isAnnotationPresent(this.annotationType) ? type
                    .getAnnotation(this.annotationType) : this
                    .readAnnotation(type.getSuperclass());
        }
        return result;
    }

}
